package com.wgzx.modbus.serialize;

import com.wgzx.modbus.constant.FunctionCode;
import com.wgzx.modbus.entity.*;
import com.wgzx.modbus.entity.exception.ModbusDecoderException;
import com.wgzx.modbus.entity.response.*;
import io.netty.buffer.ByteBuf;

public class ModbusFrameSerialization implements Serialization<ModbusReqFrame<ModbusReq>, ModbusRspFrame<ModbusRsp>> {

    public static final ModbusFrameSerialization INSTANCE = new ModbusFrameSerialization();

    @Override
    public void serialize(ModbusReqFrame<ModbusReq> msg, ByteBuf buf) {
        MbapHeader header = msg.getHeader();
        buf.writeShort(header.getTransactionIdentifier());
        buf.writeShort(header.getProtocolIdentifier());
        buf.writeShort(header.getLength());
        buf.writeByte(header.getUnitIdentifier());
        ModbusReq body = msg.getBody();
        body.encode(buf);
    }

    @Override
    public ModbusRspFrame<ModbusRsp> deserialize(ByteBuf buf) {
        int transactionIdentifier = buf.readUnsignedShort();
        int protocolIdentifier = buf.readUnsignedShort();
        int length = buf.readUnsignedShort();
        short unitIdentifier = buf.readUnsignedByte();
        MbapHeader header = new MbapHeader(transactionIdentifier, protocolIdentifier, length, unitIdentifier);

        FunctionCode functionCode = FunctionCode.valueOf(buf.readUnsignedByte());
        ModbusRsp body;
        switch (functionCode) {
            case READ_COILS:
            case READ_COILS_ERROR:
                body = new ReadCoilsResponse(functionCode);
                break;
            case READ_DISCRETE_INPUTS:
            case READ_DISCRETE_INPUTS_ERROR:
                body = new ReadDiscreteInputsResponse(functionCode);
                break;
            case READ_HOLDING_REGISTERS:
            case READ_HOLDING_REGISTERS_ERROR:
                body = new ReadHoldingRegistersResponse(functionCode);
                break;
            case READ_INPUT_REGISTERS:
            case READ_INPUT_REGISTERS_ERROR:
                body = new ReadInputRegistersResponse(functionCode);
                break;
            case WRITE_SINGLE_COIL:
            case WRITE_SINGLE_COIL_ERROR:
                body = new WriteSingleCoilResponse(functionCode);
                break;
            case WRITE_SINGLE_REGISTER:
            case WRITE_SINGLE_REGISTER_ERROR:
                body = new WriteSingleRegisterResponse(functionCode);
                break;
            case WRITE_MULTIPLE_COILS:
            case WRITE_MULTIPLE_COILS_ERROR:
                body = new WriteMultipleCoilsResponse(functionCode);
                break;
            case WRITE_MULTIPLE_REGISTERS:
            case WRITE_MULTIPLE_REGISTERS_ERROR:
                body = new WriteMultipleRegistersResponse(functionCode);
                break;
            default:
                throw new ModbusDecoderException("unknown function code: " + functionCode.getCode());
        }

        if (functionCode.isError()) {
            body.decodeError(buf);
        } else {
            body.decode(buf);
        }

        assert buf.readableBytes() == 0;
        return new ModbusRspFrame<>(header, body);
    }
}
